<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>

  <!-- <script src="http://unpkg.com/vue@next"></script> -->

</head>

<body>
  <div id="app">
    <h1>{{title}}</h1>
  </div>

  <script>
    // 1. 基本结构
    const Vue = {
      // 扩展性
      createRenderer({
        querySelector,
        insert
      }) {
        // 返回渲染器
        return {
          creaeApp(options) {
            return {
              mount(selector) {
                // console.log('mount')
                // 1. 找到宿主元素
                const parent = querySelector(selector)

                // 2. 渲染页面
                if (!options.render) {
                  //   2.1 处理tempalte：编译
                  options.render = this.compile(parent.innerHTML)
                }

                // setip 和其他选项
                if (options.setup) {
                  this.setupState = options.setup()
                }
                if (options.data) {
                  this.data = options.data()
                }

                const proxy = new Proxy(this, {
                  get(target, key) {
                    // 先从setup中取，如果取不到，再从data中取
                    if (target.setupState && key in target.setupState) {
                      // return target.setupState[key]
                      return Reflect.get(target.setupState, key)
                    } else {
                      // return target.data[key]
                      return Reflect.get(target.data, key)
                    }
                    // return target[key]
                  },
                  set(target, key, val) {
                    if (target.setupState && key in target.setupState) {
                      // target.setupState[key] = val
                      return Reflect.set(target.setupState, key, val)
                    } else {
                      // target.data[key] = val
                      return Reflect.set(target.data, key, val)
                    }
                  }
                })

                //   2.2 用户直接编写render（先不实现）
                // const el = options.render.call(options.data())

                // 更新数据
                this.update = effect(function () {
                  const el = options.render.call(proxy)

                  // 3. 追加到宿主

                  // parent.appendChild(el)
                  insert(el, parent)
                })
                // this.update()

              },

              compile(template) {
                // 返回一个render函数
                // parse -> ast
                // generate -> ast -> render
                return function render() {
                  const h3 = document.createElement('h3')
                  h3.textContent = this.title
                  return h3
                }
              }
            }
          }
        }

      },
      createApp(options) {
        // 创建一个web平台特有渲染器
        const renderer = Vue.createRenderer({
          querySelector(sel) {
            return document.querySelector(sel)
          },
          insert(el, parent) {
            parent.innerHTML = ''
            parent.appendChild(el)
          }
        })
        return renderer.creaeApp(options)
      }
    }
  </script>


  <script>
    // 能够拦截用户对代理对象的访问，从而再值发生变化的时候做出响应
    function reactive(obj) {
      // vue2
      // Object.defineProperty(obj, {})
      // 缺点：递归效率问题、数组问题、动态属性新增和删除Vue.set()、class\collection数据结构不支持
      return new Proxy(obj, {
        get(target, key) {
          console.log('get key', key)
          // 建立依赖关系
          track(target, key)
          return Reflect.get(target, key)
          // return target[key]
        },
        set(target, key, val) {
          console.log('set')
          // target[key] = val
          Reflect.set(target, key, val)
          // 通知更新
          trigger(target, key)
          // app.update()
        }
      })

    }
  </script>

  <script>
    // 建立映射关系： 依赖dep - 组件更新函数
    // vue2: 组件有watcher
    // vue3： 创建map结构 {target, {key: [update1, update2]}}


    // 调用effect ，首先执行fn
    const effextStack = []

    function effect(fn) {
      const eff = function () {
        try {
          effextStack.push(eff)
          fn()
        } finally {
          effextStack.pop()
        }
      }
      // 立即调用
      eff()

      return eff
    }

    const targetMap = {}
    // 建立target、key和effextStack存储的副作用函数之间的关系
    function track(target, key) {
      const effect = effextStack[effextStack.length - 1]
      // 判断target为key的对象存不存在
      let map = targetMap[target]
      if (!map) {
        // 首次get这个target , 不存在就创建
        map = targetMap[target] = {}
      }

      let deps = map[key]
      if (!deps) {
        deps = map[key] = []
      }

      // 映射关系建立
      if (deps.indexOf(effect) === -1) {
        deps.push(effect)
      }
      // targetMap[target][key] = [effect]
    }

    // 从
    function trigger(target, key) {
      const map = targetMap[target]
      if (map) {
        const deps = map[key]

        if (deps) {
          deps.forEach(dep => dep())
        }
      }
    }

    // const obj = reactive({
    //   foo: 'foo',
    //   bar:'bar'
    // })

    // effect(() => {
    //   // 触发响应式数据
    //   console.log(obj.foo)
    // })

    // effect(() => {
    //   // 触发响应式数据
    //   console.log(obj.foo, obj.bar)
    // })

    // obj.foo = 'fooo------'
  </script>

  <script>
    const app = Vue.createApp({
      // data() {
      //   return {
      //     title: 'hello, vue3 YK菌'
      //   }
      // },
      setup() {
        const state = reactive({
          title: 'vue3 hello YK菌'
        })

        // effect(() => {
        //   setTimeout(() => {
        //     state.title = 'hehehe 2秒后 YK菌'
        //   }, 2000)
        // })

        setTimeout(() => {
          state.title = 'hehehe 2秒后 YK菌'
        }, 2000)

        return state
      }
    })

    app.mount('#app') // 挂载+更新机制
  </script>
</body>

</html>